// Copyright 2019 The rhyme_lph Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Q [author] rhyme_lph
// C [email]  rhymelph@gmail.com
// S [github] https://github.com/rhymelph

import 'package:base_plugin/base_plugin.dart';
import 'package:finance_app/generated/l10n.dart';
import 'package:finance_app/src/common/config.dart';
import 'package:finance_app/src/entities/depreciation_entity.dart';
import 'package:finance_app/src/entities/expense_detail_entity.dart';
import 'package:finance_app/src/entities/fixed_asset_entity.dart';
import 'package:finance_app/src/entities/ocr_attachment_entity.dart';
import 'package:finance_app/src/entities/setting_asset_category_entity.dart';
import 'package:finance_app/src/entities/setting_data_entity.dart';
import 'package:finance_app/src/entities/transaction_entity.dart';
import 'package:finance_app/src/http_service/http_asset_service.dart';
import 'package:finance_app/src/http_service/http_category_service.dart';
import 'package:finance_app/src/http_service/http_expense_service.dart';
import 'package:finance_app/src/mixin/from_attach_mixin.dart';
import 'package:finance_app/src/mixin/init_edit_data_mixin.dart';
import 'package:finance_app/src/router_gen/router_gen_providers.dart';
import 'package:finance_app/src/utils/date_time_utils.dart';
import 'package:finance_app/src/widgets/bottom_sheet/calendar_bottom_sheet.dart';
import 'package:finance_app/src/widgets/bottom_sheet/currency_bottom_sheet.dart';
import 'package:finance_app/src/widgets/bottom_sheet/delete_info_bottom_sheet.dart';
import 'package:finance_app/src/widgets/bottom_sheet/depreciation_method_select_bottom_sheet.dart';
import 'package:finance_app/src/widgets/bottom_sheet/got_it_info_bottom_sheet.dart';
import 'package:finance_app/src/widgets/bottom_sheet/got_it_tip_bottom_sheet.dart';
import 'package:finance_app/src/widgets/bottom_sheet/list_select_bottom_sheet.dart';
import 'package:flutter/material.dart';

import '../date_report_provider.dart';

class NewFixedAssetProvider extends BaseProvider<FixedAssetEntity>
    with InitEditDataMixin, FormAttachMixin {
  List<String> menuList = [];

  SettingDataCurrency currency;

  TextEditingController assetNameController = TextEditingController();
  FocusNode assetNameFocusNode = FocusNode();

  TextEditingController costController = TextEditingController();
  FocusNode costFocusNode = FocusNode();

  TextEditingController bookValueController = TextEditingController();
  FocusNode bookValueFocusNode = FocusNode();

  bool get bookValueIsEmpty =>
      bookValueController.text == null || bookValueController.text == '';

  SettingAssetCategoryEntity category;

  DepreciationEntity depreciationMethod;

  TransactionEntity number;

  TextEditingController lifeSpanController = TextEditingController(text: '36');
  FocusNode lifeSpanFocusNode = FocusNode();

  bool isDisposed = false;

  bool isSold = false;

  bool isDeleted = false;

  bool canUpdate = false;

  bool expenseMode = false;

  @override
  Future get dataFuture => id != null ? HttpAssetService.showAsset(id) : null;

  @override
  void initState() {
    super.initState();
    bookValueFocusNode.addListener(() {
      if (!bookValueIsEmpty) {
        if (!bookValueController.text.endsWith('%')) {
          bookValueController.text = '${bookValueController.text}%';
        }
      }
    });
  }

  @override
  void dispose() {
    super.dispose();
    assetNameController.dispose();
    assetNameFocusNode.dispose();
    costController.dispose();
    costFocusNode.dispose();
    bookValueController.dispose();
    bookValueFocusNode.dispose();
    lifeSpanController.dispose();
    lifeSpanFocusNode.dispose();
  }

  void unFocus() {
    assetNameFocusNode.unfocus();
    costFocusNode.unfocus();
    bookValueFocusNode.unfocus();
    lifeSpanFocusNode.unfocus();
  }

  void onSelectMenu(String value) {
    unFocus();
    switch (value) {
      case 'Edit':
        onChangeEditMode();
        break;
      case 'Delete':
        onDelete();
        break;
      case 'Mark As Sold':
        onMarkAsSold();
        break;
      case 'Mark As Disposed':
        onMarkAsDisposed();
        break;
      case 'View Sales Details':
        onViewSalesDetails();
        break;
      case 'View Disposal Details':
        onViewDisposalDetails();
        break;
    }
  }

  void onSave() async {
    unFocus();
    String assetName = assetNameController.text;
    if (assetName.isEmpty) {
      showToast(S.current.please_enter_asset_name);
      return;
    }
    if (category == null && !expenseMode) {
      showToast(S.current.please_select_category);
      return;
    }
    if (date == null) {
      showToast(S.current.please_select_purchase_date);
      return;
    }
    if (currency == null) {
      showToast(S.current.please_select_currency);
      return;
    }
    String cost = costController.text;
    if (cost.isEmpty) {
      showToast(S.current.please_enter_cost);
      return;
    }

    String lifeSpan = lifeSpanController.text;
    FixedAssetEntity orderEntity;

    if (expenseMode) {
      data.name = assetName;
      data.lifespan = int.parse(lifeSpan);
      data.bookValueAfter = bookValueIsEmpty
          ? 0
          : num.parse(
              bookValueController.text.replaceAll('%', ''), (e) => null);
      obtainContext((context) {
        Navigator.pop(context, data);
      });

      return;
    }

    List<int> attachmentIds = await uploadFiles();
    if (attachmentIds == null) return;

    if (isEdit) {
      await post(
          HttpAssetService.updateAsset(
            id,
            name: assetName,
            category: category?.key,
            type: number?.transactionType,
            type_id: number?.transactionableId,
            purchased_at: DateTimeUtils.formatDateTime(date, 'yyyy-MM-dd'),
            currency_code: currency?.key,
            price: num.parse(cost, (e) => null),
            book_value_after: bookValueIsEmpty
                ? 0
                : num.parse(
                    bookValueController.text.replaceAll('%', ''), (e) => null),
            depreciation_type: depreciationMethod?.key,
            lifespan: num.parse(lifeSpan),
            attachment_ids: attachmentIds,
          ),
          loadingTip: S.current.loading,
          autoHandler: AutoHandler.Toast, onSuccess: (result) {
        orderEntity = getEntityFromResult<FixedAssetEntity>(result);
      });
    } else {
      await post(
          HttpAssetService.createAsset(
            name: assetName,
            category: category?.key,
            type: number?.transactionType,
            type_id: number?.transactionableId,
            purchased_at: DateTimeUtils.formatDateTime(date, 'yyyy-MM-dd'),
            currency_code: currency?.key,
            price: num.parse(cost, (e) => null),
            book_value_after: bookValueIsEmpty
                ? 0
                : num.parse(
                    bookValueController.text.replaceAll('%', ''), (e) => null),
            depreciation_type: depreciationMethod?.key,
            lifespan: num.parse(lifeSpan),
            attachment_ids: attachmentIds,
          ),
          loadingTip: S.current.loading,
          autoHandler: AutoHandler.Toast, onSuccess: (result) {
        orderEntity = getEntityFromResult<FixedAssetEntity>(result);
      });
    }

    if (orderEntity != null) {
      isEdit = false;
      id = orderEntity.id;
      data = transformData(orderEntity, false);
      Providers.get<DateReportProvider>(RouterGenProviders.dateReportPage, null)
          ?.onTry();
      update();
    }
  }

  void onChangePurchaseDate() {
    unFocus();
    obtainContext?.call((context) async {
      date = await showCalendarBottomSheet(context, date);
      update();
    });
  }

  void onChangeCurrency() {
    unFocus();
    obtainContext?.call((context) async {
      currency = await showCurrencyBottomSheet(context, currency);
      update();
    });
  }

  void onChangeCategory() {
    unFocus();
    obtainContext?.call((context) async {
      final result =
          await showFixedCategorySelectBottomSheet(context, category);
      if (result != null) {
        category = result;
        update();
      }
    });
  }

  void onChangeLinkedExpense() async {
    unFocus();
    final result =
        await Routes.navigateTo(RouterGenProviders.linkedExpensePage, params: {
      'select': number,
    });
    if (result != null) {
      number = result;
      ExpenseDetailEntity expense;
      await post(HttpExpenseService.showExpense(number.transactionableId),
          autoHandler: AutoHandler.Toast,
          loadingTip: S.current.loading, onSuccess: (result) {
        expense = getEntityFromResult<ExpenseDetailEntity>(result);
      });
      costController.text =
          (expense.total - expense.inclusiveTaxAmount).toStringAsNotFixed(2);
      update();
    }
  }

  void onShowCostInfo() {
    unFocus();
    obtainContext?.call((context) {
      showFixedAssetCostInfoBottomSheet(context);
    });
  }

  void onChangeDepreciationMethod() {
    unFocus();
    obtainContext?.call((context) async {
      depreciationMethod = await showDepreciationMethodSelectBottomSheet(
          context, depreciationMethod);
      update();
      print(depreciationMethod.key);
    });
  }

  void onShowTotalUsefulLife() {
    obtainContext?.call((context) {
      showTotalUsefulLifeTipInfoBottomSheet(context);
    });
  }

  @override
  Future get editDataFuture => dataFuture;

  @override
  TextEditingController get numberCtl => null;

  @override
  String get type => 'asset';

  void onDelete() {
    obtainContext?.call((context) async {
      bool isSuccess = await showDeleteFixedAssetBottomSheet(context);

      if (isSuccess == true) {
        isSuccess = false;
        await post(HttpAssetService.deleteAsset(id),
            autoHandler: AutoHandler.Toast,
            loadingTip: S.current.loading, onSuccess: (result) {
          isSuccess = true;
        });
        if (isSuccess) {
          await showDeleteFixedAssetSuccessTipBottomSheet(context);
          Providers.get<DateReportProvider>(
                  RouterGenProviders.dateReportPage, null)
              ?.onRequestRefresh();
          Navigator.of(context).pop();
        }
      }
    });
  }

  void onMarkAsSold() async {
    SettingDataCategory salesOfFixedAssetPurpose;
    await post(HttpCategoryService.cashInCategories(),
        loadingTip: 'Loading',
        autoHandler: AutoHandler.Dialog, onSuccess: (result) async {
      RProvider.myProvider.globalMap[ConfigKey.cashInCategory] =
          BaseConfig.entityBase.formJson<List<SettingDataCategory>>(
              result.data[BaseConfig.presenterBase.dataKey]);
      await RProvider.myProvider.updateGlobalMap();

      salesOfFixedAssetPurpose =
          getEntityFromResult<List<SettingDataCategory>>(result)
              .firstWhere((element) => element.key == 'fa_disposal');
    });
    await Routes.navigateTo(RouterGenProviders.newCashInPage, params: {
      'assetId': id,
      'assetNumber': data.refNumber,
      'assetName': assetNameController.text,
      'category': salesOfFixedAssetPurpose,
      'total': data.total,
      'unit': currency
    });

    loadData();
  }

  void onMarkAsDisposed() async {
    await Routes.navigateTo(RouterGenProviders.disposalOfFixedAssetPage,
        params: {
          'assetName': assetNameController.text,
          'id': id,
        });
    loadData();
  }

  void onViewSalesDetails() async {
    Routes.navigateTo(RouterGenProviders.salesOfFixedAssetPage, params: {
      'isViewOnly': true,
      'assetName': assetNameController.text,
      'salesId': data.payments.length > 0 ? data.payments.first.id : null,
      'salesDate': data.sellAt,
      'salesPrice': data.sellPrice,
      'unit': currency,
      'notes': data.notes,
      'attachments': data.soldAttachmentsInfo,
      'id': id,
    });
  }

  void onViewDisposalDetails() {
    Routes.navigateTo(RouterGenProviders.disposalOfFixedAssetPage, params: {
      'isViewOnly': true,
      'assetName': assetNameController.text,
      'disposalDate': data.disposedAt,
      'unit': currency,
      'notes': data.notes,
      'attachments': data.disposedAttachmentsInfo,
      'id': id,
    });
  }

  void onShowEditTotalTip() {
    obtainContext?.call((context) async {
      await showTotalUsefulLifeTipInfoBottomSheet(context);
      canUpdate = true;
      update();
    });
  }

  @override
  FutureOr<FixedAssetEntity> transformData(FixedAssetEntity d, bool isCache) {
    assetNameController.text = d.name;
    category = Config.settingData.assetCategory
        .firstWhere((element) => element.key == d.category, orElse: () => null);
    date = DateTimeUtils.toDateTime(d.purchasedAt, 'yyyy-MM-dd');
    currency = Config.settingData.currency.firstWhere(
        (element) => element.key == d.currencyCode,
        orElse: () => null);
    costController.text = d.price;
    if (d.bookValueAfter != null) {
      bookValueController.text = '${d.bookValueAfter}%';
    } else {
      bookValueController.text = '0%';
    }
    depreciationMethod = Config.settingData.depreciationType.firstWhere(
        (element) => element.key == d.depreciationType,
        orElse: () => null);
    lifeSpanController.text = d.lifespan.toString();
    isDisposed = d.status == 'disposed';
    isSold = d.status == 'sold';
    isSold = d.status == 'sold';
    isDeleted = d.status == 'deleted';
    if (isDisposed) {
      menuList = ['View Disposal Details'];
    } else if (isSold) {
      menuList = ['View Sales Details'];
    } else if (isDeleted) {
      menuList = [];
    } else {
      menuList = [
        'Edit',
        'Delete',
        'Mark As Sold',
        'Mark As Disposed',
      ];
    }
    if (type != null && d.typeId != null && d.number != null) {
      number = TransactionEntity()
        ..transactionableId = d.typeId
        ..number = d.number;
    }
    return super.transformData(d, isCache);
  }

  void initExpenseMode(FixedAssetEntity fixedAsset) {
    if (fixedAsset == null) return;
    expenseMode = true;
    data = fixedAsset;
    transformData(fixedAsset, true);
  }

  @override
  List<OcrAttachmentEntity> get attachments => data?.attachmentsInfo;
}
